package com.zmx.job.admin.core.thread;

import com.zmx.job.admin.core.conf.MallAdminConfig;
import com.zmx.job.admin.core.dto.JobDistributeInfo;
import com.zmx.job.admin.enums.RoutStrategyEnum;
import com.zmx.job.admin.core.queue.JobQueue;
import com.zmx.job.admin.entity.MallJobInfo;
import com.zmx.job.admin.repository.MallJobInfoRepository;
import com.zmx.job.admin.utils.CronUtil;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 任务定时执行处理类
 * @ClassName: MallJobSchedulerHelper
 * @Author zhaomxr
 * @Date 2021-12-20 16:00
 */
@Slf4j
public class JobSchedulerHelper {

    private static JobSchedulerHelper instance = new JobSchedulerHelper();

    public static JobSchedulerHelper getInstance() {
        return instance;
    }

    private Thread thread;

    private volatile boolean scheduleThreadToStop = false;

    /**
     * 补偿时间，用于系统加载延时时间
     */
    private int COMPENSATETIME = 4000;

    public void start() {
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!scheduleThreadToStop) {
                    Connection conn = null;
                    Boolean connAutoCommit = null;
                    PreparedStatement preparedStatement = null;
                    MallAdminConfig mallAdminConfig = MallAdminConfig.getMallAdminConfig();

                    try {
                        conn = mallAdminConfig.getDataSource().getConnection();
                        connAutoCommit = conn.getAutoCommit();
                        conn.setAutoCommit(false);

                        //PreparedStatement防止sql注入，利用for update实现锁
                        PreparedStatement prepareStatement = conn.prepareStatement("SELECT * FROM mall_lock WHERE lock_name = 'mall_lock' for update");
                        prepareStatement.execute();

                        long nowTime = System.currentTimeMillis();
                        int pagesize = mallAdminConfig.getMaxMeanwhile();
                        //下次运行时间小于当前时间的任务
                        MallJobInfoRepository jobInfoRepository = mallAdminConfig.getMallJobInfoRepository();
                        List<MallJobInfo> mallJobInfos = jobInfoRepository.scheduleJobQuery(nowTime, pagesize);

                        if (mallJobInfos != null && mallJobInfos.size() > 0) {
                            System.out.println(new Date());
                            for (MallJobInfo mallJobInfo : mallJobInfos) {
                                //将当前任务放入自定义队列队尾
                                String name = mallJobInfo.getExecutorHandler();
                                String routStrategy = mallJobInfo.getExecutorRouteStrategy();
                                //看是否开启了重试机制，重试次数没有设置或者次数小于1，默认重试1次
                                int failRetryCount = 0;
                                if (mallAdminConfig.isRequestRetry() && mallAdminConfig.getRequestRetryCount() > 0) {
                                    if (mallJobInfo.getExecutorFailRetryCount() == null || mallJobInfo.getExecutorFailRetryCount() <= 0) {
                                        failRetryCount = mallAdminConfig.getRequestRetryCount();
                                    } else {
                                        failRetryCount = mallJobInfo.getExecutorFailRetryCount();
                                    }
                                }

                                JobQueue.offer(new JobDistributeInfo(name, RoutStrategyEnum.getEnumByCode(routStrategy), failRetryCount));

                                //更新当前任务下次刷新时间
                                String scheduleConf = mallJobInfo.getScheduleConf();
                                mallJobInfo.setTriggerLastTime(CronUtil.getLastTriggerTime(scheduleConf));
                                mallJobInfo.setTriggerNextTime(CronUtil.getNextTriggerTime(scheduleConf));
                                jobInfoRepository.save(mallJobInfo);
                            }
                        }
                    } catch (SQLException e) {
                        if (!scheduleThreadToStop) {
                            log.error(">>>>>>>>>>> mall-job, JobScheduleHelper#scheduleThread error:{}", e);
                        }
                    } finally {

                        // commit
                        if (conn != null) {
                            try {
                                conn.commit();
                            } catch (SQLException e) {
                                if (!scheduleThreadToStop) {
                                    log.error(e.getMessage(), e);
                                }
                            }
                            try {
                                conn.setAutoCommit(connAutoCommit);
                            } catch (SQLException e) {
                                if (!scheduleThreadToStop) {
                                    log.error(e.getMessage(), e);
                                }
                            }
                            try {
                                conn.close();
                            } catch (SQLException e) {
                                if (!scheduleThreadToStop) {
                                    log.error(e.getMessage(), e);
                                }
                            }
                        }

                        // close PreparedStatement
                        if (null != preparedStatement) {
                            try {
                                preparedStatement.close();
                            } catch (SQLException e) {
                                if (!scheduleThreadToStop) {
                                    log.error(e.getMessage(), e);
                                }
                            }
                        }
                    }
                }
            }
        });

        thread.setName("JobSchedulerHelper");
        thread.setDaemon(true);
        thread.start();
    }


    public void toStop() {

        // 1、stop schedule
        scheduleThreadToStop = true;
        try {
            // wait
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }
        if (thread.getState() != Thread.State.TERMINATED) {
            // interrupt and wait
            thread.interrupt();
            try {
                thread.join();
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
            }
        }
    }
}
